package apidemo;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Date;

import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;

import apidemo.AccountInfoPanel.PortfolioModel;

import com.ib.controller.ApiConnection.ILogger;
import com.ib.client.Order;
import com.ib.controller.ApiController;
import com.ib.controller.ApiController.IConnectionHandler;
import com.ib.controller.Formats;
import com.ib.controller.NewContract;
import com.ib.controller.NewOrder;
import com.ib.controller.NewOrderState;
import com.ib.controller.OrderStatus;
import com.ib.controller.Position;
import com.ib.controller.ApiController.IAccountHandler;
import com.ib.controller.ApiController.IBulletinHandler;
import com.ib.controller.ApiController.IOrderHandler;
import com.ib.controller.ApiController.IPositionHandler;
import com.ib.controller.ApiController.ITimeHandler;
import com.ib.controller.Types.NewsType;

public class ScheduleTest /*extends TimerTask */ implements IAccountHandler,
		IOrderHandler, IConnectionHandler {
	private PositionModel m_model = new PositionModel();
	private boolean m_complete;
	static ApiDemo INSTANCE = new ApiDemo();
	private final JTextArea m_msg = new JTextArea();
	private final JTextArea m_inLog = new JTextArea();
	private final JTextArea m_outLog = new JTextArea();
	private final Logger m_inLogger = new Logger(m_inLog);
	private final Logger m_outLogger = new Logger(m_outLog);
	private final ApiController m_controller = new ApiController(this,
			m_inLogger, m_outLogger);
	// private final ArrayList<String> m_acctList = new ArrayList<String>();
	// private ArrayList<String> m_accounts = new ArrayList<String>();
	private String m_selAcct = "";
	private PortfolioModel m_portfolioModel = new PortfolioModel();

	private final static long ONCE_PER_DAY = 1000 * 60 * 60 * 24;

	// private final static int ONE_DAY = 1;
	private final static int TWO_AM = 2;
	private final static int ZERO_MINUTES = 0;

	@Override
	public void error(Exception e) {
		show(e.toString());
	}

	@Override
	public void message(int id, int errorCode, String errorMsg) {
		show(id + " " + errorCode + " " + errorMsg);
	}

	@Override
	public void connected() {
		show("connected");
		// m_connectionPanel.m_status.setText( "connected");

		m_controller.reqCurrentTime(new ITimeHandler() {
			@Override
			public void currentTime(long time) {
				show("Server date/time is " + Formats.fmtDate(time * 1000));
			}
		});

		m_controller.reqBulletins(true, new IBulletinHandler() {
			@Override
			public void bulletin(int msgId, NewsType newsType, String message,
					String exchange) {
				String str = String.format(
						"Received bulletin:  type=%s  exchange=%s", newsType,
						exchange);
				show(str);
				show(message);
			}
		});
	}

	@Override
	public void disconnected() {
		show("disconnected");
		// m_connectionPanel.m_status.setText( "disconnected");
	}

	@Override
	public void accountList(ArrayList<String> list) {
		show("Received account list");
		// m_acctList.clear();
		// m_acctList.addAll( list);
	}

	@Override
	public void show(final String str) {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				m_msg.append(str);
				m_msg.append("\n\n");

				Dimension d = m_msg.getSize();
				m_msg.scrollRectToVisible(new Rectangle(0, d.height, 1, 1));
			}
		});
	}
/*
	@Override
	public void run() {
		System.out.println("Start Job " + System.currentTimeMillis());

		try {
			m_controller.connect("127.0.0.1", 7496, 0);

			onAcctChanged();

			// ApiDemo.INSTANCE.controller().reqPositions( m_model);

			// onAcctChanged();

			onClosePosition();
		} catch (Exception e) {
			System.out.println("Error in Run(); " + e.getLocalizedMessage());
			System.out.println("Error in Run(); " + e.getStackTrace());
		} finally {
			m_controller.disconnect();
		}

		System.out.println("End Job " + System.currentTimeMillis());
	}
*/
	private static Date getTomorrowMorning2AM() {

		Date date2am = new java.util.Date();
		date2am.setHours(TWO_AM);
		date2am.setMinutes(ZERO_MINUTES);

		return date2am;
	}

	// call this method from your servlet init method
	public static void startTask() {
		ScheduleTest task = new ScheduleTest();
		Timer timer = new Timer();
		//timer.schedule(task, getToDay9PM(), ONCE_PER_DAY);
		
		
	}

	public static void main(String args[]) {
		startTask();
		System.out.format("Task scheduled.....%n");
	}

	// PRIVATE
	private final static long fONCE_PER_DAY = 1000 * 60 * 60 * 24;

	private final static int fONE_DAY = 1;
	private final static int fCLOSE_HOUR = 4;
	private final static int fCLOSE_MINUTE = 45;

	private static long getSecondsUntilClose() {
		Date _final = getToDay9PM();
		//Date _final = getTomorrowMorning4am();
		Date _initial =  new Date();
		
		long seconds = (_final.getTime() - _initial.getTime());
		
		return seconds;
	}
	
	private static Date getToDay9PM() {
		Calendar today = new GregorianCalendar();
		Calendar result = new GregorianCalendar(today.get(Calendar.YEAR),
				today.get(Calendar.MONTH), today.get(Calendar.DATE), 23,
				41);
		System.out.println("The job will be called at " + result.getTime());
		return result.getTime();
	}
	
	private static Date getTomorrowMorning4am() {
		Calendar tomorrow = new GregorianCalendar();
		tomorrow.add(Calendar.DATE, fONE_DAY);
		Calendar result = new GregorianCalendar(tomorrow.get(Calendar.YEAR),
				tomorrow.get(Calendar.MONTH), tomorrow.get(Calendar.DATE),
				fCLOSE_HOUR, fCLOSE_MINUTE);
		System.out.println("The job will be called at " + result.getTime());
		return result.getTime();
	}

	protected void onAcctChanged() {
		String selAcct = "DU203316";
		m_selAcct = selAcct;
		m_portfolioModel.clear();
		ApiDemo.INSTANCE.controller().reqAccountUpdates(true, m_selAcct, this);
	}

	protected void onClosePosition() {
		// String account = (String)m_accounts.get(0);
		String account = "DU203316";
		// int i = m_portTable.getSelectedRow();
		int i = m_portfolioModel.getRowCount();
		if (account != null) {
			Position position = m_portfolioModel.getPosition(i);
			// ApiDemo.INSTANCE.controller().exerciseOption(account,
			// position.contract(), m_combo.getSelectedItem(), m_qty.getInt(),
			// m_override.isSelected() );

			// Create a global OrderID for simplicity
			// It will be submitted to the IB Server for executing the trade
			// To be incremented for the next trade with calls to the IB Server
			//int newOrderID = 1;

			// Create a contract object to reverse the position - use the same
			// contract!
			NewContract order_ticker = position.contract();

			// Create an order object
			Order order = new Order();

			// if we're currently long then we need to sell otherwise buy
			if (position.position() > 0) {
				order.m_action = "SELL";
			} else if (position.position() < 0) {
				order.m_action = "BUY";
			}
			order.m_totalQuantity = Math.abs(position.position());
			order.m_orderType = "MKT";
			order.m_transmit = true;
			order.m_referencePriceType = 0;
			// order.m_orderRef = "Auto close position at 3:45am" ;
			order.m_orderRef = "Auto";
			order.m_tif = "DAY";

			NewOrder newOrderExecution = new NewOrder(order);

			// Send the Market Order request
			ApiDemo.INSTANCE.controller().placeOrModifyOrder(order_ticker,
					newOrderExecution, new IOrderHandler() {

						@Override
						public void orderState(NewOrderState orderState) {
							ApiDemo.INSTANCE.show("This is the orderState: "
									+ orderState.status().toString());
						}

						@Override
						public void handle(int errorCode, String errorMsg) {
							ApiDemo.INSTANCE
									.show("Hey, Error happened for the action: "
											+ errorCode + " " + errorMsg);

						}

						@Override
						public void orderStatus(OrderStatus status, int filled,
								int remaining, double avgFillPrice,
								long permId, int parentId,
								double lastFillPrice, int clientId,
								String whyHeld) {
							// TODO Auto-generated method stub

						}

					});

		}
	}

	@Override
	public void orderState(NewOrderState orderState) {
		// TODO Auto-generated method stub

	}

	@Override
	public void orderStatus(OrderStatus status, int filled, int remaining,
			double avgFillPrice, long permId, int parentId,
			double lastFillPrice, int clientId, String whyHeld) {
		// TODO Auto-generated method stub

	}

	@Override
	public void handle(int errorCode, String errorMsg) {
		// TODO Auto-generated method stub

	}

	@Override
	public void accountValue(String account, String key, String value,
			String currency) {
		// TODO Auto-generated method stub

	}

	@Override
	public void accountTime(String timeStamp) {
		// TODO Auto-generated method stub

	}

	@Override
	public void accountDownloadEnd(String account) {
		// TODO Auto-generated method stub

	}

	@Override
	/** Receive position. */
	public synchronized void updatePortfolio(Position position) {
		if (position.account().equals(m_selAcct)) {
			m_portfolioModel.update(position);
		}
	}

	private static class Logger implements ILogger {
		final private JTextArea m_area;

		Logger(JTextArea area) {
			m_area = area;
		}

		@Override
		public void log(final String str) {
			SwingUtilities.invokeLater(new Runnable() {
				@Override
				public void run() {
					// m_area.append(str);
					//
					// Dimension d = m_area.getSize();
					// m_area.scrollRectToVisible( new Rectangle( 0, d.height,
					// 1, 1) );
				}
			});
		}
	}

	private class PositionModel extends AbstractTableModel implements
			IPositionHandler {
		HashMap<PositionKey, PositionRow> m_map = new HashMap<PositionKey, PositionRow>();
		ArrayList<PositionRow> m_list = new ArrayList<PositionRow>();

		@Override
		public void position(String account, NewContract contract,
				int position, double avgCost) {
			PositionKey key = new PositionKey(account, contract.conid());
			PositionRow row = m_map.get(key);
			if (row == null) {
				row = new PositionRow();
				m_map.put(key, row);
				m_list.add(row);
			}
			row.update(account, contract, position, avgCost);

			if (m_complete) {
				m_model.fireTableDataChanged();
			}
		}

		@Override
		public void positionEnd() {
			m_model.fireTableDataChanged();
			m_complete = true;
		}

		public void clear() {
			m_map.clear();
			m_list.clear();
			fireTableDataChanged();
		}

		@Override
		public int getRowCount() {
			return m_map.size();
		}

		@Override
		public int getColumnCount() {
			return 4;
		}

		@Override
		public String getColumnName(int col) {
			switch (col) {
			case 0:
				return "Account";
			case 1:
				return "Contract";
			case 2:
				return "Position";
			case 3:
				return "Avg Cost";
			default:
				return null;
			}
		}

		@Override
		public Object getValueAt(int rowIn, int col) {
			PositionRow row = m_list.get(rowIn);

			switch (col) {
			case 0:
				return row.m_account;
			case 1:
				return row.m_contract.description();
			case 2:
				return row.m_position;
			case 3:
				return Formats.fmt(row.m_avgCost);
			default:
				return null;
			}
		}
	}

	private static class PositionKey {
		String m_account;
		int m_conid;

		PositionKey(String account, int conid) {
			m_account = account;
			m_conid = conid;
		}

		@Override
		public int hashCode() {
			return m_account.hashCode() + m_conid;
		}

		@Override
		public boolean equals(Object obj) {
			PositionKey other = (PositionKey) obj;
			return m_account.equals(other.m_account)
					&& m_conid == other.m_conid;
		}
	}

	private static class PositionRow {
		String m_account;
		NewContract m_contract;
		int m_position;
		double m_avgCost;

		void update(String account, NewContract contract, int position,
				double avgCost) {
			m_account = account;
			m_contract = contract;
			m_position = position;
			m_avgCost = avgCost;
		}
	}

}
